package com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.dp.mndataif002;

import com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.dp.DomUtil;
import com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.dp.ErrorCodeConstant;
import com.alibaba.cirtus.ots.application.adapter.ability.model.sdo.CustomerAddressSDO;
import com.alibaba.cirtus.ots.application.adapter.ability.model.sdo.CustomerSDO;
import com.alibaba.cirtus.ots.application.adapter.ability.model.sdo.CustomerSaleScopeSDO;
import com.alibaba.citrus.ots.common.annotation.ProcessInvoker;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.epoch.app.bcorder.model.dto.OrganizationSDO;
import com.epoch.app.crplatformenhance.domain.customer.model.Customer;
import com.epoch.app.otsapplicationadaptersdk.contants.OtsApplicationAdapterSdkConstants;
import com.epoch.app.otsapplicationadaptersdk.dp.DpCustmer;
import com.epoch.app.otsapplicationadaptersdk.dp.MnDataCustmerResponse;
import com.epoch.app.otsapplicationadaptersdk.dp.MnDataCustmerSDO;
import com.epoch.app.otsapplicationadaptersdk.dp.mndataif002company.dto.BatchDoHandlerRequest;
import com.epoch.app.otsapplicationadaptersdk.dp.mndataif002company.dto.DoHandlerRequest;
import com.epoch.app.otsapplicationadaptersdk.dp.mndataif002company.service.MnDataIf002CompanyService;
import com.epoch.app.otsapplicationadaptersdk.model.dto.RestDpCustmerDataCompanyRequest;
import com.epoch.app.otsapplicationadaptersdk.service.DpService;
import com.google.common.collect.Sets;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author zhoubc
 * @description: 售达方销售公司同步
 * @date 2021/9/25 1:08
 */
@Component
public class MnDataIf002CompanyV2ServiceImpl implements MnDataIf002CompanyService {
    Log log = Log.getLogger(MnDataIf002CompanyV2ServiceImpl.class);
    @Resource
    private MnDataIf002SyncUtilsV2 mnDataIf002SyncUtils;

    @Resource
    private MnDataIf002SDOBuilderV2 mnDataIf002SdoBuilder;

    @Resource
    private DpService dpService;

    @ProcessInvoker(remark = "batchMnDataIf002CompanyService", errorCode = "", printLog = true)
    @Override
    public Object batchDoHandler(BatchDoHandlerRequest batchDoHandlerRequest) {
        if(batchDoHandlerRequest.getPullDays() == null || batchDoHandlerRequest.getPullDays() <= 0){
            return Result.fail("","入参pullDays错误");
        }
        String beginDate = batchDoHandlerRequest.getDs();
        for(int i = 1; i<=batchDoHandlerRequest.getPullDays(); i++){
            DoHandlerRequest doHandlerRequest = new DoHandlerRequest();
            doHandlerRequest.setFromSys(batchDoHandlerRequest.getFromSys());
            doHandlerRequest.setPageNum(batchDoHandlerRequest.getPageNum());
            doHandlerRequest.setPageSize(batchDoHandlerRequest.getPageSize());
            doHandlerRequest.setDs(beginDate);
            Object object = doHandler(doHandlerRequest);
            beginDate = DomUtil.getRollbackDate(DomUtil.parse(beginDate),1);
        }
        return Result.success(null);
    }

    @ProcessInvoker(remark = "MnDataIf002CompanyService", errorCode = "", printLog = true)
    @Override
    public Object doHandler(DoHandlerRequest doHandlerRequest) {
        // 构造中台查询参数
        RestDpCustmerDataCompanyRequest param = buildMnQueryMap(doHandlerRequest);
        //调用中台接口获取客户主数据
        MnDataCustmerResponse mnDataCustmerResponse = getMnDataCustomerResponse(param);
        MnDataCustmerSDO data = mnDataCustmerResponse.getData();
        //sls告警日志
        if (Objects.isNull(data) || CollectionUtils.isEmpty(data.getRows())) {
            log.info("MnDataIf002CompanyService_pull_data_result_is_null,query_params={}", JSONObject.toJSON(param));
            return mnDataCustmerResponse;
        }
        // 计算总页数
        int totalPage = getTotalPage(data);
        log.info("MnDataIf002CompanyService_total={},page={},query_params={}", data.getTotalNum(), totalPage, JSONObject.toJSON(param));

        long before = System.currentTimeMillis();
        //分页同步客户主数据、销售范围、收货地址
        for (int current = 1; current <= totalPage; current++) {
            long start = System.currentTimeMillis();
            log.info("MnDataIf002CompanyService_doHandler_page_start,current_page={}", current);
            param.setPageNum(String.valueOf(current));
            MnDataCustmerResponse customerRes = getMnDataCustomerResponse(param);
            log.info("MnDataIf002CompanyService_doHandler_rest_getMnDataCustomerResponse={}", JSONObject.toJSONString(customerRes));
            if (Objects.isNull(customerRes.getData()) || CollectionUtils.isEmpty(customerRes.getData().getRows())) {
                continue;
            }
            List<DpCustmer> mnCustomers = customerRes.getData().getRows();
            try {
                syncCustomer(mnCustomers);
            } catch (Exception e){
                log.error("MnDataIf002CompanyService_doHandler_exception_current_page_error ", e);
                log.error("MnDataIf002CompanyService_doHandler_exception_current_page={},query_params={}", current,JSONObject.toJSON(param));
            }
            log.info("MnDataIf002CompanyService_doHandler_page_end,current_page={},elapsed_time={}", current, System.currentTimeMillis() - start);
        }
        log.info("MnDataIf002CompanyService_doHandler_end,elapsed_time={}", System.currentTimeMillis() - before);
        return mnDataCustmerResponse;
    }

    @NotNull
    private MnDataCustmerResponse getMnDataCustomerResponse(RestDpCustmerDataCompanyRequest param) {
        MnDataCustmerResponse mnDataCustmerResponse = null;
        int retryTimes = 0;
        String errCode = null;
        do {
            if (ErrorCodeConstant.RATE_LIMIT.equals(errCode)) {
                try {
                    Thread.sleep(60000);
                } catch (InterruptedException e) {
                    log.error("MnDataIf002CompanyServiceImpl#doHandler InterruptedException", e);
                }
            }
            try {
                mnDataCustmerResponse = dpService.restDpCustmerDataCompany(param);
            } catch (Exception e) {
                log.error("MnData_rest_req_occur_exception,restDpCustmerDataCompany_errorMsg={}",e.getMessage());
                throw e;
            }
            errCode = mnDataCustmerResponse.getErrCode();
        } while (!"0".equals(errCode) && retryTimes++ < 10);
        return mnDataCustmerResponse;
    }

    private void syncCustomer(List<DpCustmer> mnCustomers) {
        //拉取数据同步销售组织过滤配置
        if (StringUtils.isNotBlank(OtsApplicationAdapterSdkConstants.pull_customer_data_organization())
                && !"-1".equals(OtsApplicationAdapterSdkConstants.pull_customer_data_organization())) {

            List filterOrgList = Arrays.stream(OtsApplicationAdapterSdkConstants.pull_customer_data_organization().split(",")).collect(Collectors.toList());
            mnCustomers = mnCustomers.stream().filter(x->filterOrgList.contains(x.getVkorg())).collect(Collectors.toList());
        }
        if (CollectionUtils.isEmpty(mnCustomers)) {
            log.info("syncCurrentPageOfCustomerAddress_filterOrgData_mnCompanyCustomer_is_null, filterOrgList={}",OtsApplicationAdapterSdkConstants.pull_customer_data_organization());
            return;
        }

        // 1、查询出所有组织
        Map<String, OrganizationSDO> organizationMapByCustomers = mnDataIf002SdoBuilder.getOrganizationMapByCustomers(mnCustomers);
        log.info("syncCustomer,request,mnCustomers:{}",mnCustomers);
        log.info("syncCustomer,response,organizationMapByCustomers:{}",organizationMapByCustomers);
        if (Objects.isNull(organizationMapByCustomers)) {
            return;
        }

        List<CustomerSDO> customerSDOs = Lists.newArrayList();
        List<CustomerSaleScopeSDO> customerSaleScopeSDOs = Lists.newArrayList();
        // 2、过滤出新增和修改的客户、售达方、送达方
        for (DpCustmer dpCustomer : mnCustomers) {
            CustomerSDO customerSDO = mnDataIf002SdoBuilder.buildCustomerMainDataSDO(dpCustomer, organizationMapByCustomers);
            if (Objects.isNull(customerSDO)) {
                continue;
            }
            customerSDOs.add(customerSDO);
            CustomerSaleScopeSDO customerSaleScopeSDO = mnDataIf002SdoBuilder.buildCustomerSaleScopeSDO(dpCustomer, organizationMapByCustomers);
            if (Objects.isNull(customerSaleScopeSDO)) {
                continue;
            }
            log.info("002company_syncCustomer_buildCustomerSaleScopeSDO_result={},pull_origin_data={}", JSONObject.toJSON(customerSaleScopeSDO), JSONObject.toJSON(dpCustomer));
            customerSaleScopeSDOs.add(customerSaleScopeSDO);
            // 12.22 逻辑：售达方接口不保存地址信息
        }
        // 批量保存或者更新客户主档数据
        batchSaveAndUpdateCustomers(customerSDOs);
        // 批量保存或者更新客户销售范围
        batchSaveAndUpdateCustomerScopes(customerSaleScopeSDOs);
    }

    private void batchSaveAndUpdateAddresses(List<CustomerAddressSDO> customerAddressSDOs) {
        if (CollectionUtils.isEmpty(customerAddressSDOs)) {
            return;
        }
        List<CustomerAddressSDO> existAddressList = mnDataIf002SyncUtils.batchQueryCustomerAddress(customerAddressSDOs);
        Map<String, CustomerAddressSDO> existAddressMap = existAddressList.stream().collect(Collectors.toMap((it -> mnDataIf002SyncUtils.getAddressCombineKey(it)), a -> a, (a, b) -> a));
        List<CustomerAddressSDO> notExistAddresses = customerAddressSDOs.stream().filter(it -> !existAddressMap.containsKey(mnDataIf002SyncUtils.getAddressCombineKey(it))).collect(Collectors.toList());
        Map<String, CustomerAddressSDO> addressUpdatedMap = customerAddressSDOs.stream().filter(it -> existAddressMap.containsKey(mnDataIf002SyncUtils.getAddressCombineKey(it))).collect(Collectors.toMap(it -> mnDataIf002SyncUtils.getAddressCombineKey(it), a -> a, (a, b) -> a));
        if (CollectionUtils.isNotEmpty(notExistAddresses)) {
            Boolean addressSave = mnDataIf002SyncUtils.batchCreateCustomerAddresses(notExistAddresses);
            log.info("客户送达方批量新增结果:{}", addressSave);
        }
        if (CollectionUtils.isNotEmpty(existAddressList)) {
            Boolean addressUpdated = mnDataIf002SyncUtils.batchUpdateCustomerAddresses(existAddressList, addressUpdatedMap);
            log.info("客户送达方批量更新结果:{}", addressUpdated);
        }
    }

    private void batchSaveAndUpdateCustomerScopes(List<CustomerSaleScopeSDO> customerSaleScopeSDOs) {
        if (CollectionUtils.isEmpty(customerSaleScopeSDOs)) {
            return;
        }
        List<CustomerSaleScopeSDO> existScopeList = mnDataIf002SyncUtils.batchQueryCustomerScopes(customerSaleScopeSDOs);
        Map<String, CustomerSaleScopeSDO> existScopeMap = existScopeList.stream().collect(Collectors.toMap((it -> mnDataIf002SyncUtils.getScopeCombineKey(it)), a -> a, (a, b) -> a));
        List<CustomerSaleScopeSDO> notExistScopes = customerSaleScopeSDOs.stream().filter(it -> !existScopeMap.containsKey(mnDataIf002SyncUtils.getScopeCombineKey(it))).collect(Collectors.toList());
        Map<String, CustomerSaleScopeSDO> updatedMap = customerSaleScopeSDOs.stream().filter(it -> existScopeMap.containsKey(mnDataIf002SyncUtils.getScopeCombineKey(it))).collect(Collectors.toMap(it -> mnDataIf002SyncUtils.getScopeCombineKey(it), a -> a, (a, b) -> a));
        if (CollectionUtils.isNotEmpty(notExistScopes)) {
            Boolean scopeSave = mnDataIf002SyncUtils.batchCreateCustomerScopes(notExistScopes);
        }
        if (CollectionUtils.isNotEmpty(existScopeList)) {
            Boolean scopeUpdate = mnDataIf002SyncUtils.batchUpdateCustomerScopes(existScopeList, updatedMap);
        }
    }

    private void batchSaveAndUpdateCustomers(List<CustomerSDO> customerSDOs) {
        if (CollectionUtils.isEmpty(customerSDOs)) {
            return;
        }

        // 去除重复的客户信息
        List<CustomerSDO> noRepeatCustomerSDOs = Lists.newArrayList();
        Set<String> customerCodeSet = Sets.newHashSet();
        for (CustomerSDO customerSDO : customerSDOs) {
            if (customerCodeSet.contains(customerSDO.getCode())) {
                continue;
            }
            customerCodeSet.add(customerSDO.getCode());
            noRepeatCustomerSDOs.add(customerSDO);
        }

        List<String> customerCodes = new ArrayList<>(customerCodeSet);
        List<Customer> existCustomers = mnDataIf002SyncUtils.batchQueryCustomerMainData(customerCodes);
        Map<String, Customer> existCustomerMap = existCustomers.stream().collect(Collectors.toMap(Customer::getCode, a -> a, (a, b) -> a));
        Map<String, CustomerSDO> updateMap = noRepeatCustomerSDOs.stream().filter(it -> existCustomerMap.containsKey(it.getCode())).collect(Collectors.toMap(CustomerSDO::getCode, a -> a, (a, b) -> a));
        List<CustomerSDO> notExistCustomers = noRepeatCustomerSDOs.stream().filter(it -> !existCustomerMap.containsKey(it.getCode())).collect(Collectors.toList());
        //处理新增的客户
        if (CollectionUtils.isNotEmpty(notExistCustomers)) {
            Boolean save = mnDataIf002SyncUtils.batchCreateCustomers(notExistCustomers);
        }
        if (CollectionUtils.isNotEmpty(existCustomers)) {
            Boolean update = mnDataIf002SyncUtils.batchUpdateCustomers(existCustomers, updateMap);
        }
    }


    public int getTotalPage(MnDataCustmerSDO data) {
        Integer totalNum = data.getTotalNum();
        Integer pageSize = data.getPageSize();
        int totalPage = 1;
        if (totalNum % pageSize == 0) {
            totalPage = totalNum / pageSize;
        } else {
            totalPage = (totalNum / pageSize) + 1;
        }
        return totalPage;
    }

    private RestDpCustmerDataCompanyRequest buildMnQueryMap(DoHandlerRequest doHandlerRequest) {
        RestDpCustmerDataCompanyRequest dataCompanyRequest = RestDpCustmerDataCompanyRequest.builder()
                .ds(doHandlerRequest.getDs())
                .fromSys(doHandlerRequest.getFromSys())
                .pageNum(doHandlerRequest.getPageNum())
                .pageSize(doHandlerRequest.getPageSize()).build();
        return dataCompanyRequest;
    }
}
